package com.sgq.vue.security.filter;

import com.sgq.vue.redis.impl.RedisServiceImpl;
import com.sgq.vue.security.exception.CustomerAuthenticationException;
import com.sgq.vue.security.handler.MyAuthenticationFailureHandler;
import com.sgq.vue.security.service.CustomerUserDetailsService;
import com.sgq.vue.utils.system.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author: 83778
 * @date: 2023-10-30 16:51
 * @desc:
 */
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private CustomerUserDetailsService userDetailsService;

    @Autowired
    private RedisServiceImpl redisServiceImpl;

    @Autowired
    private MyAuthenticationFailureHandler failureHandler ;

    @Autowired
    private JwtUtils jwtUtils;

    @Value("${spring.loginUri}")
    private String loginUri;

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {
        String requestURI = request.getRequestURI();
        System.out.println("====>"+loginUri);
        System.out.println("拦截requestURI ====> " + requestURI);
        try {
            if (!loginUri.equals(requestURI)){
                this.verifyToken(request);
            }
        }catch (AuthenticationException e){
            failureHandler.onAuthenticationFailure(request,response,e);
        }

        doFilter(request, response , chain);
    }

    private void verifyToken(HttpServletRequest request){
        //1.从请求头获取token
        String token = request.getHeader(jwtUtils.getTokenHeader());
        //2.判断是否获取到token
        if (ObjectUtils.isEmpty(token)){
            token = request.getParameter(jwtUtils.getTokenHeader());
        }
        //3.如果都是空的，抛出未获取到token的异常
        if (ObjectUtils.isEmpty(token)){
            throw new CustomerAuthenticationException("未获取到token");
        }
        //4.获取到token，判断是否以'Bearer '作为token的前缀
        if (!token.startsWith(jwtUtils.getTokenHead())){
            throw new CustomerAuthenticationException("token格式错误");
        }
        //5.从redis中获取token
        String redisToken = redisServiceImpl.get(redisServiceImpl.getPrefix()+token,String.class);
        if (ObjectUtils.isEmpty(redisToken)){
            throw new CustomerAuthenticationException("token不存在");
        }
        //6.对比redisToken与前端token
        if (!redisToken.equals(token)){
            throw new CustomerAuthenticationException("token不一致");
        }
        //7.解析token
        Boolean expired = jwtUtils.isTokenExpired(token);
        if (expired){
            throw new CustomerAuthenticationException("token已超时");
        }
        String username = jwtUtils.getUsernameFromToken(token);
        if (ObjectUtils.isEmpty(username)){
            throw new CustomerAuthenticationException("token解析失败...");
        }
        //8.成功解析token
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        //9.判断用户信息是否为空
        if (ObjectUtils.isEmpty(userDetails)){
            throw new CustomerAuthenticationException("数据库不存在该用户");
        }
        //10.创建身份认证信息
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());

        //11.设置请求信息
        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
        //12.认证信息加入到身份认证上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
    }
}
